using System;
using System.Collections.Generic;
using System.Text;

using Crank.Core.Strings;

namespace Crank.Core
{
    public class NounSpace
    {
        private PluralizationAssumption lean;
        private IDictionary<String, Noun> nouns;

        private NounFactory factory;
        private NounResolver resolver;

        public static NounSpace FromWordList(IEnumerable<string> words)
        {
            NounSpace fromWords = new NounSpace();
            foreach (string word in words)
            {
                fromWords.Add(word);
            }
            return fromWords;
        }

        public NounSpace() : this(PluralizationAssumption.AssumePlural) { }

        public NounSpace(PluralizationAssumption lean)
        {
            this.lean = lean;
            this.nouns = new Dictionary<String, Noun>();
            Initialize();
        }

        private void Initialize()
        {
            factory = new NounFactory(lean);
            resolver = new NounResolver(lean);
        }

        public void Add(string word)
        {
            Add(factory.CreateFrom(word));
        }

        private void Add(Noun noun)
        {
            TryAdd(noun, noun.Singular);
            TryAdd(noun, noun.Plural);
        }

        private void TryAdd(Noun noun, string CheckWord)
        {
            if (Contains(CheckWord))
            {
                Noun other = nouns[CheckWord];
                if (!other.Equals(noun))
                {
                    Noun final = resolver.TryResolve(other, noun);
                    if (!other.Equals(final))
                    {
                        Remove(other);
                        Insert(final);
                    }
                }
            }
            else
            {
                Insert(noun);
            }
        }

        private void Remove(Noun noun)
        {
            nouns.Remove(noun.Singular);
            nouns.Remove(noun.Plural);
        }

        private void Insert(Noun noun)
        {
            nouns[noun.Singular] = noun;
            nouns[noun.Plural] = noun;
        }

        public bool Contains(string word)
        {
            return nouns.ContainsKey(word);
        }

        public string SingleOf(string word)
        {
            return Contains(word) ?
                nouns[word].Singular :
                word;
        }

        public string PluralOf(string word)
        {
            return Contains(word) ?
                nouns[word].Plural :
                word;
        }

        public IEnumerable<String> Singles
        {
            get
            {
                return Enumerables.Unique(
                    Enumerables.Collect<Noun, String>(
                        nouns.Values,
                        delegate(Noun n)
                        {
                            return n.Singular;
                        }));
            }
        }

        public IEnumerable<String> Plurals
        {
            get
            {
                return Enumerables.Unique(
                    Enumerables.Collect<Noun, String>(
                        nouns.Values,
                        delegate(Noun n)
                        {
                            return n.Plural;
                        }));
            }
        }

        public bool IsSingular(string word)
        {
            return SingleOf(word).Equals(word);
        }

        public bool IsPlural(string word)
        {
            return PluralOf(word).Equals(word);
        }
    }
}
